﻿@using Fluxor.Blazor.Web.Components;
@using Microsoft.IdentityModel.Tokens;
@using SimpleIdServer.IdServer.Website.Resources;
@using SimpleIdServer.IdServer.Website.Stores.ClientStore;
@inherits FluxorComponent
@inject IState<UpdateClientState> updateClientState
@inject Radzen.DialogService dialogService
@inject NotificationService notificationService
@inject IDispatcher dispatcher

<RadzenSteps>
    <Steps>
        <RadzenStepsItem Text="@Global.Key">
            <div class="row gy-4">
                @foreach (var key in KeyTypes)
                {
                    <div class="col-md-6">
                        <RadzenCard @onclick="() => SelectKey(key)" class="@(SelectedKeyType != null && SelectedKeyType.Name == key.Name ? "selected selectable" : "selectable")">
                            <div class="row">
                                <div class="col-md-3">
                                    <RadzenImage Path="@key.PictureUrl" Style="width: 60px" />
                                </div>
                                <div class="col">
                                    <h5>@key.Name</h5>
                                    <p class="text-muted">@(new MarkupString(key.ShortDescription))</p>
                                </div>
                            </div>
                        </RadzenCard>
                    </div>
                }
            </div>

            <hr />

            @if (SelectedKeyType != null)
            {
                <div>
                    <h5><RadzenIcon Icon="info" /> @SelectedKeyType.Name</h5>
                    <p class="text-muted">@(new MarkupString(SelectedKeyType.Description))</p>
                </div>
            }
        </RadzenStepsItem>
        <RadzenStepsItem Text="@Global.Generate" Disabled="@(SelectedKeyType == null)">
            @if (!updateClientState.Value.IsUpdating && !string.IsNullOrWhiteSpace(updateClientState.Value.ErrorMessage))
            {
                <RadzenAlert AllowClose="false" AlertStyle="AlertStyle.Danger" ShowIcon="true" Variant="Variant.Flat" Shade="Shade.Lighter">@updateClientState.Value.ErrorMessage</RadzenAlert>
            }

            @if (keyIsGenerated)
            {
                <RadzenAlert AllowClose="false" AlertStyle="AlertStyle.Success" ShowIcon="true" Variant="Variant.Flat" Shade="Shade.Lighter">@Global.KeyGenerated</RadzenAlert>
            }

            @switch(SelectedKeyType.Usage)
            {
                case SimpleIdServer.IdServer.Constants.JWKUsages.Sig:
                    <RadzenTemplateForm Submit=@GenerateSigKey TItem="SigKeyGenerationForm" Data=@sigKeyGeneration>
                        <!-- Key Identifier -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Identifier</RadzenText>
                            <RadzenTextBox Name="KeyId" @bind-Value="@sigKeyGeneration.KeyId" Class="w-100"></RadzenTextBox>
                            <RadzenRequiredValidator Component="KeyId" Text="@Global.IdentifierIsRequired"></RadzenRequiredValidator>
                        </div>
                        <!-- Key Type -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.KeyType</RadzenText>
                            <RadzenDropDown Name="KeyType" Class="w-100"
                                    Data=@sigKeyTypes
                                    TValue="SecurityKeyTypes"
                                    Change="@OnSigSecurityKeyTypeChange"
                                    @bind-Value=@sigKeyGeneration.KeyType
                                    TextProperty="Name" ValueProperty="KeyType" />
                            <RadzenRequiredValidator Component="KeyType" Text="@Global.KeyTypeRequired"></RadzenRequiredValidator>
                        </div>
                        <!-- Alg -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Algorithm</RadzenText>
                            <RadzenDropDown Name="Alg" Class="w-100"
                                            Data=@signingAlgs
                                            TValue="string"
                                            @bind-Value=@sigKeyGeneration.Alg />
                            <RadzenRequiredValidator Component="Alg" Text="@Global.AlgorithmRequired"></RadzenRequiredValidator>
                        </div>
                        <RadzenButton class="mt-1" Variant="Variant.Flat" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" Text="@Global.Generate" />
                    </RadzenTemplateForm>
                    break;
                case SimpleIdServer.IdServer.Constants.JWKUsages.Enc:
                    <RadzenTemplateForm Submit=@GenerateEncKey TItem="EncKeyGenerationForm" Data=@encKeyGeneration>
                        <!-- Key Identifier -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Identifier</RadzenText>
                            <RadzenTextBox Name="KeyId" @bind-Value="@encKeyGeneration.KeyId" Class="w-100"></RadzenTextBox>
                            <RadzenRequiredValidator Component="KeyId" Text="@Global.IdentifierIsRequired"></RadzenRequiredValidator>
                        </div>
                        <!-- Key Type -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.KeyType</RadzenText>
                            <RadzenDropDown Name="KeyType" Class="w-100"
                                    Data=@encKeyTypes
                                    TValue="SecurityKeyTypes"
                                    Change="@OnEncSecurityKeyTypeChange"
                                    @bind-Value=@encKeyGeneration.KeyType
                                    TextProperty="Name" ValueProperty="KeyType" />
                            <RadzenRequiredValidator Component="KeyType" Text="@Global.KeyTypeRequired"></RadzenRequiredValidator>
                        </div>
                        <!-- Alg -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Algorithm</RadzenText>
                            <RadzenDropDown Name="Alg" Class="w-100"
                                    Data=@encryptionAlgs
                                    TValue="string"
                                    @bind-Value=@encKeyGeneration.Alg />
                            <RadzenRequiredValidator Component="Alg" Text="@Global.AlgorithmRequired"></RadzenRequiredValidator>
                        </div>
                        <!-- Enc -->
                        <div>
                            <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Encryption</RadzenText>
                            <RadzenDropDown Name="Enc" Class="w-100"
                                    Data=@SimpleIdServer.IdServer.Constants.AllEncryptions
                                    TValue="string"
                                    @bind-Value=@encKeyGeneration.Enc />
                            <RadzenRequiredValidator Component="Enc" Text="@Global.EncryptionRequired"></RadzenRequiredValidator>
                        </div>
                        <RadzenButton class="mt-1" Variant="Variant.Flat" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" Text="@Global.Generate" />
                    </RadzenTemplateForm>
                    break;
            }
        </RadzenStepsItem>
        <RadzenStepsItem Text="@Global.Summary" Disabled="@(generatedSigKey == null && generatedEncKey == null)">
            @if(generatedSigKey != null)
            {                
                <!-- Key Identifier -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Identifier</RadzenText>
                    <RadzenTextBox Name="KeyId" @bind-Value="@generatedSigKey.KeyId" Disabled=true Class="w-100"></RadzenTextBox>
                </div>
                <!-- Key Type -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.KeyType</RadzenText>
                    <RadzenDropDown Name="KeyType" Class="w-100"
                                Data=@sigKeyTypes
                                    TValue="SecurityKeyTypes"
                                    @bind-Value=@generatedSigKey.KeyType
                                    TextProperty="Name" ValueProperty="KeyType"
                                    Disabled=true/>
                </div>
                <!-- Alg -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Algorithm</RadzenText>
                    <RadzenDropDown Name="Alg" Class="w-100"
                                    Data=@SimpleIdServer.IdServer.Constants.AllSigningAlgs
                                    TValue="string"
                                    Disabled=true
                                    @bind-Value=@generatedSigKey.Alg />
                </div>
                <!-- Public Pem -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.PublicKey</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedSigKey.Pem.PublicKey Disabled=true></RadzenTextArea>
                </div>
                <!-- Private Pem -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.PrivateKey</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedSigKey.Pem.PrivateKey Disabled=true></RadzenTextArea>
                </div>
                <!-- Json Web Key -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Jwk</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedSigKey.JsonWebKey Disabled=true></RadzenTextArea>
                </div>
                <RadzenButton Click="@(args => SaveSigKey())" class="mt-1" Variant="Variant.Flat" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" Text="@Global.Confirm" />
            }
            else
            {
                <!-- Key Identifier -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Identifier</RadzenText>
                    <RadzenTextBox Name="KeyId" @bind-Value="@generatedEncKey.KeyId" Disabled=true Class="w-100"></RadzenTextBox>
                </div>
                <!-- Key Type -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.KeyType</RadzenText>
                    <RadzenDropDown Name="KeyType" Class="w-100"
                                    Data=@encKeyTypes
                                    TValue="SecurityKeyTypes"
                                @bind-Value=@generatedEncKey.KeyType
                                    TextProperty="Name" ValueProperty="KeyType"
                                    Disabled=true/>
                </div>
                <!-- Alg -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Algorithm</RadzenText>
                    <RadzenDropDown Name="Alg" Class="w-100"
                                    Data=@SimpleIdServer.IdServer.Constants.AllEncAlgs
                                    TValue="string"
                                    Disabled=true
                                @bind-Value=@generatedEncKey.Alg />
                </div>
                <!-- Enc -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Encryption</RadzenText>
                    <RadzenDropDown Name="Alg" Class="w-100"
                                Data=@SimpleIdServer.IdServer.Constants.AllEncryptions
                                TValue="string"
                                Disabled=true
                                @bind-Value=@generatedEncKey.Enc />
                </div>
                <!-- Public Pem -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.PublicKey</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedEncKey.Pem.PublicKey Disabled=true></RadzenTextArea>
                </div>
                <!-- Private Pem -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.PrivateKey</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedEncKey.Pem.PrivateKey Disabled=true></RadzenTextArea>
                </div>
                <!-- Json Web Key -->
                <div>
                    <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@Global.Jwk</RadzenText>
                    <RadzenTextArea Class="w-100" Rows=10 Value=@generatedEncKey.JsonWebKey Disabled=true></RadzenTextArea>
                </div>
                <RadzenButton Click="@(args => SaveEncKey())" class="mt-1" Variant="Variant.Flat" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Success" Text="@Global.Confirm" />                
            }
        </RadzenStepsItem>
    </Steps>
</RadzenSteps>

@code {
    [Parameter]
    public Client Client { get; set; }

    bool keyIsGenerated { get; set; } = false;

    static Dictionary<SecurityKeyTypes, IEnumerable<string>> mappingSecurityKeyToSigAlg = new Dictionary<SecurityKeyTypes, IEnumerable<string>>
    {
        { SecurityKeyTypes.CERTIFICATE, new List<string> { SecurityAlgorithms.RsaSha256, SecurityAlgorithms.RsaSha384, SecurityAlgorithms.RsaSha512  } },
        { SecurityKeyTypes.RSA, new List<string> { SecurityAlgorithms.RsaSha256, SecurityAlgorithms.RsaSha384, SecurityAlgorithms.RsaSha512  } },
        { SecurityKeyTypes.ECDSA, new List<string> { SecurityAlgorithms.EcdsaSha256, SecurityAlgorithms.EcdsaSha384, SecurityAlgorithms.EcdsaSha512  } }
    };

    static Dictionary<SecurityKeyTypes, IEnumerable<string>> mappingSecurityKeyToEncAlg = new Dictionary<SecurityKeyTypes, IEnumerable<string>>
    {
        { SecurityKeyTypes.CERTIFICATE, new List<string> { SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.RsaOAEP } },
        { SecurityKeyTypes.RSA, new List<string> { SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.RsaOAEP } }
    };

    record SigKeyGenerationForm
    {
        public string KeyId { get; set; } = null!;
        public SecurityKeyTypes KeyType { get; set; } = SecurityKeyTypes.RSA;
        public string Alg { get; set; } = SecurityAlgorithms.RsaSha256;
    }

    record EncKeyGenerationForm
    {
        public string KeyId { get; set; } = null!;
        public SecurityKeyTypes KeyType { get; set; } = SecurityKeyTypes.RSA;
        public string Alg { get; set; } = SecurityAlgorithms.RsaPKCS1;
        public string Enc { get; set; } = SecurityAlgorithms.Aes128CbcHmacSha256;
    }

    record KeyType
    {
        public string Name { get; set; } = null!;
        public string ShortDescription { get; set; } = null!;
        public string Description { get; set; } = null!;
        public string Usage { get; set; } = null!;
        public string PictureUrl { get; set; } = null!;
    }

    record KeyTypeInfo
    {
        public string Name { get; set; } = null!;
        public SecurityKeyTypes KeyType { get; set; }
    }

    bool isKeyGenerated { get; set; } = false;
    GenerateSigKeySuccessAction generatedSigKey { get; set; } = null;
    GenerateEncKeySuccessAction generatedEncKey { get; set; } = null;
    IEnumerable<string> signingAlgs { get; set; } = mappingSecurityKeyToSigAlg[SecurityKeyTypes.RSA];
    IEnumerable<string> encryptionAlgs { get; set; } = mappingSecurityKeyToEncAlg[SecurityKeyTypes.RSA];

    List<KeyTypeInfo> sigKeyTypes = new List<KeyTypeInfo>
    {
        new KeyTypeInfo { KeyType = SecurityKeyTypes.RSA, Name = "RSA" },
        new KeyTypeInfo { KeyType = SecurityKeyTypes.CERTIFICATE, Name = "CERTIFICATE" },
        new KeyTypeInfo { KeyType = SecurityKeyTypes.ECDSA, Name = "ECDSA" }
    };

    List<KeyTypeInfo> encKeyTypes = new List<KeyTypeInfo>
    {
        new KeyTypeInfo { KeyType = SecurityKeyTypes.RSA, Name = "RSA" },
        new KeyTypeInfo { KeyType = SecurityKeyTypes.CERTIFICATE, Name = "CERTIFICATE" }
    };

    ICollection<KeyType> KeyTypes { get; set;} = new List<KeyType>
    {
        new KeyType { Name = Global.SignatureKey, ShortDescription = Global.SignatureKeyParameters, Description = Global.GenerateSignatureKeyHelper, Usage = SimpleIdServer.IdServer.Constants.JWKUsages.Sig, PictureUrl = "_content/SimpleIdServer.IdServer.Website/images/SigKey.png" },
        new KeyType { Name = Global.EncryptionKey, ShortDescription = Global.Jwe, Description = Global.GenerateEncryptionKeyHelper, PictureUrl = "_content/SimpleIdServer.IdServer.Website/images/EncKey.png", Usage = SimpleIdServer.IdServer.Constants.JWKUsages.Enc }
    };

    KeyType? SelectedKeyType { get; set; } = null;
    SigKeyGenerationForm sigKeyGeneration = new SigKeyGenerationForm();
    EncKeyGenerationForm encKeyGeneration = new EncKeyGenerationForm();

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        SubscribeToAction<GenerateSigKeySuccessAction>((act) =>
        {
            keyIsGenerated = true;
            generatedSigKey = act;
            generatedEncKey = null;
            StateHasChanged();
        });
        SubscribeToAction<GenerateEncKeySuccessAction>((act) =>
        {
            keyIsGenerated = true;
            generatedEncKey = act;
            generatedSigKey = null;
            StateHasChanged();
        });
        SubscribeToAction<AddSigKeySuccessAction>((act) =>
        {
            notificationService.Notify(new NotificationMessage { Severity = NotificationSeverity.Success, Summary = Global.SigKeyAdded });
            dialogService.Close();
            StateHasChanged();
        });
        SubscribeToAction<AddEncKeySuccessAction>((act) =>
        {
            notificationService.Notify(new NotificationMessage { Severity = NotificationSeverity.Success, Summary = Global.EncKeyAdded });
            dialogService.Close();
            StateHasChanged();
        });
        dispatcher.Dispatch(new StartGenerateClientKeyAction());
    }

    void SelectKey(KeyType keyType)
    {
        SelectedKeyType = keyType;
    }

    void OnSigSecurityKeyTypeChange(object securityKeyType)
    {
        var st = (SecurityKeyTypes)securityKeyType;
        signingAlgs = mappingSecurityKeyToSigAlg[st];
        sigKeyGeneration.Alg = signingAlgs.ElementAt(0);
        StateHasChanged();
    }

    void OnEncSecurityKeyTypeChange(object securityKeyType)
    {
        var st = (SecurityKeyTypes)securityKeyType;
        encryptionAlgs = mappingSecurityKeyToEncAlg[st];
        encKeyGeneration.Alg = encryptionAlgs.ElementAt(0);
        StateHasChanged();
    }

    void GenerateSigKey(SigKeyGenerationForm form)
    {
        keyIsGenerated = false;
        dispatcher.Dispatch(new GenerateSigKeyAction { Alg = form.Alg, ClientId = Client.ClientId, KeyId = form.KeyId, KeyType = form.KeyType });
    }

    void GenerateEncKey(EncKeyGenerationForm form)
    {
        keyIsGenerated = false;
        dispatcher.Dispatch(new GenerateEncKeyAction { Alg = form.Alg, ClientId = Client.ClientId, KeyId = form.KeyId, KeyType = form.KeyType, Enc = form.Enc });
    }

    void SaveSigKey()
    {
        dispatcher.Dispatch(new AddSigKeyAction { Alg = generatedSigKey.Alg, ClientId = Client.ClientId, Credentials = generatedSigKey.Credentials, KeyId = generatedSigKey.KeyId, KeyType = generatedSigKey.KeyType });
    }

    void SaveEncKey()
    {
        dispatcher.Dispatch(new AddEncKeyAction { Alg = generatedEncKey.Alg, ClientId = Client.ClientId, Credentials = generatedEncKey.Credentials, KeyId = generatedEncKey.KeyId, KeyType = generatedEncKey.KeyType, Enc = generatedEncKey.Enc });
    }
}